<template>
    <div class="file-upload-container">
        <!-- 内联预览（单图、成功且可预览时显示） -->
        <div v-if="inlinePreviewEnabled" class="inline-preview" @click="triggerFileInput">
            <img :src="fileList[0].url" alt="预览" />
            <el-button class="inline-remove" type="danger" size="small" link @click.stop="removeFile(0)">删除</el-button>
        </div>

        <!-- 上传区域 -->
        <div v-else class="upload-area" :class="{
            'upload-area--dragover': isDragOver,
            'upload-area--disabled': disabled
        }" @drop="handleDrop" @dragover="handleDragOver" @dragleave="handleDragLeave" @click="triggerFileInput">
            <div class="upload-content" :class="{ 'upload-content--plain': variant === 'plain' }">
                <el-icon v-if="variant === 'withText'" class="upload-icon">
                    <Upload />
                </el-icon>
                <div v-if="variant === 'withText'" class="upload-text">
                    <p class="upload-title">点击或拖拽文件到此处上传</p>
                    <p class="upload-desc">支持 {{ allowedTypes.join(', ') }} 格式</p>
                    <p class="upload-size">文件大小不超过 {{ formatFileSize(maxSize) }}</p>
                </div>
                <el-button type="primary" :disabled="disabled" @click.stop="triggerFileInput">
                    选择文件
                </el-button>
            </div>

            <!-- 隐藏的文件输入框 -->
            <input ref="fileInput" type="file" :accept="acceptString" :multiple="multiple" @change="handleFileSelect"
                style="display: none" />
        </div>

        <!-- 文件列表 -->
        <div v-if="showFileList && fileList.length > 0" class="file-list">
            <div v-for="(file, index) in fileList" :key="`${file.uid}-${file.status}-${file.percentage}`"
                class="file-item" :class="{ 'file-item--uploading': file.status === 'uploading' }">
                <!-- 文件图标 -->
                <div class="file-icon">
                    <el-icon v-if="isImage(file)">
                        <Picture />
                    </el-icon>
                    <el-icon v-else-if="isAudio(file)">
                        <VideoPlay />
                    </el-icon>
                    <el-icon v-else-if="isDocument(file)">
                        <Document />
                    </el-icon>
                    <el-icon v-else>
                        <Files />
                    </el-icon>
                </div>

                <!-- 文件信息 -->
                <div class="file-info">
                    <div class="file-name">{{ file.name }}</div>
                    <div class="file-size">{{ formatFileSize(file.size) }}</div>

                    <!-- 上传进度 -->
                    <div v-if="file.status === 'uploading'" class="file-progress">
                        <el-progress :percentage="file.percentage || 0" :stroke-width="4" :show-text="false" />
                        <span class="progress-text">{{ file.percentage || 0 }}%</span>
                    </div>

                    <!-- 状态指示 -->
                    <div class="file-status">
                        <el-icon v-if="file.status === 'success'" class="status-success">
                            <CircleCheck />
                        </el-icon>
                        <el-icon v-else-if="file.status === 'error'" class="status-error">
                            <CircleClose />
                        </el-icon>
                        <el-icon v-else-if="file.status === 'uploading'" class="status-uploading">
                            <Loading />
                        </el-icon>
                    </div>
                </div>

                <!-- 操作按钮 -->
                <div class="file-actions">
                    <!-- 预览按钮 -->
                    <el-button v-if="file.status === 'success' && canPreview(file)" type="primary" size="small" link
                        @click="handlePreviewFile(file)">
                        <el-icon>
                            <View />
                        </el-icon>
                    </el-button>

                    <!-- 删除按钮 -->
                    <el-button type="danger" size="small" link @click="removeFile(index)"
                        :disabled="file.status === 'uploading'">
                        <el-icon>
                            <Delete />
                        </el-icon>
                    </el-button>
                </div>
            </div>
        </div>

        <!-- 文件预览对话框 -->
        <el-dialog v-model="previewVisible" title="文件预览" width="60%" :before-close="closePreview">
            <div class="preview-content">
                <!-- 图片预览 -->
                <img v-if="previewFileData && isImage(previewFileData)" :src="previewUrl" class="preview-image"
                    alt="预览图片" />

                <!-- 音频预览 -->
                <audio v-else-if="previewFileData && isAudio(previewFileData)" :src="previewUrl" controls
                    class="preview-audio" />

                <!-- PDF预览 -->
                <iframe v-else-if="previewFileData && isPdf(previewFileData)" :src="previewUrl" class="preview-pdf" />

                <!-- 其他文件 -->
                <div v-else class="preview-other">
                    <el-icon class="preview-icon">
                        <Document />
                    </el-icon>
                    <p>{{ previewFileData?.name }}</p>
                    <el-button type="primary" @click="downloadFile">下载文件</el-button>
                </div>
            </div>
        </el-dialog>
    </div>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { uploadFile as uploadFileApi } from '@/api/upload'
import {
    Upload,
    Picture,
    VideoPlay,
    Document,
    Files,
    CircleCheck,
    CircleClose,
    Loading,
    View,
    Delete
} from '@element-plus/icons-vue'

// 定义文件类型
interface UploadFile {
    uid: string
    name: string
    size: number
    type: string
    status: 'pending' | 'uploading' | 'success' | 'error'
    percentage?: number
    url?: string
    response?: any
    raw?: File
}

// 定义组件属性
interface Props {
    // 允许的文件类型
    allowedTypes?: string[]
    // 最大文件大小（字节）
    maxSize?: number
    // 是否支持多文件上传
    multiple?: boolean
    // 是否禁用
    disabled?: boolean
    // 文件列表
    modelValue?: UploadFile[]
    // 自定义上传函数
    customUpload?: (file: File) => Promise<string>
    // 组件外观：带说明(withText) 或 简洁(plain)
    variant?: 'withText' | 'plain'
    // 是否显示文件列表
    showFileList?: boolean
    // 单图内联预览（成功后直接展示图片）
    inlinePreview?: boolean
}

// 定义事件
interface Emits {
    (e: 'update:modelValue', files: UploadFile[]): void
    (e: 'change', files: UploadFile[]): void
    (e: 'success', file: UploadFile, response: any): void
    (e: 'error', file: UploadFile, error: any): void
    (e: 'remove', file: UploadFile, index: number): void
}

const props = withDefaults(defineProps<Props>(), {
    allowedTypes: () => [
        'image/*',
        'audio/*',
        'application/pdf',
        'application/msword',
        'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
        'application/vnd.ms-excel',
        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    ],
    maxSize: 10 * 1024 * 1024, // 10MB
    multiple: false,
    disabled: false,
    modelValue: () => [],
    variant: 'withText',
    showFileList: true,
    inlinePreview: false
})

const emit = defineEmits<Emits>()

// 响应式数据
const fileInput = ref<HTMLInputElement>()
const fileList = ref<UploadFile[]>([])
const isDragOver = ref(false)
const previewVisible = ref(false)
const previewFileData = ref<UploadFile | null>(null)
const previewUrl = ref('')

// 内联预览条件：启用、非多选、列表首个文件成功且为图片并存在url
const inlinePreviewEnabled = computed(() => {
    if (!props.inlinePreview || props.multiple) return false
    if (!fileList.value.length) return false
    const f = fileList.value[0]
    return !!(f && f.status === 'success' && f.url && isImage(f))
})

// 计算属性
const acceptString = computed(() => {
    return props.allowedTypes.join(',')
})

// 监听modelValue变化
watch(() => props.modelValue, (newValue) => {
    // 避免循环更新，只在真正需要更新时才更新
    if (JSON.stringify(fileList.value) !== JSON.stringify(newValue)) {
        // 避免覆盖正在上传的文件状态
        const uploadingFiles = fileList.value.filter(f => f.status === 'uploading')
        const newFiles = [...newValue]

        // 保留正在上传的文件
        uploadingFiles.forEach(uploadingFile => {
            const existingIndex = newFiles.findIndex(f => f.uid === uploadingFile.uid)
            if (existingIndex !== -1) {
                newFiles[existingIndex] = uploadingFile
            }
        })

        fileList.value = newFiles
    }
}, { immediate: true, deep: true })

// 监听fileList变化，触发更新事件
watch(fileList, (newValue) => {
    // 避免循环更新，只在真正需要更新时才触发事件
    const currentModelValue = props.modelValue || []
    if (JSON.stringify(newValue) !== JSON.stringify(currentModelValue)) {
        emit('update:modelValue', newValue)
        emit('change', newValue)
    }
}, { deep: true })

// 文件类型判断
const isImage = (file: UploadFile | File) => {
    return file.type.startsWith('image/')
}

const isAudio = (file: UploadFile | File) => {
    return file.type.startsWith('audio/')
}

const isDocument = (file: UploadFile | File) => {
    return file.type === 'application/pdf' ||
        file.type === 'application/msword' ||
        file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
        file.type === 'application/vnd.ms-excel' ||
        file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
        file.type.includes('document') ||
        file.type.includes('text')
}

const isPdf = (file: UploadFile | File) => {
    return file.type === 'application/pdf'
}

const canPreview = (file: UploadFile) => {
    return isImage(file) || isAudio(file) || isPdf(file)
}

// 文件大小格式化
const formatFileSize = (bytes: number) => {
    if (bytes === 0) return '0 B'
    const k = 1024
    const sizes = ['B', 'KB', 'MB', 'GB']
    const i = Math.floor(Math.log(bytes) / Math.log(k))
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

// 生成唯一ID
const generateUid = () => {
    return Date.now().toString(36) + Math.random().toString(36).substr(2)
}

// 触发文件选择
const triggerFileInput = () => {
    if (!props.disabled) {
        fileInput.value?.click()
    }
}

// 处理文件选择
const handleFileSelect = (event: Event) => {
    const target = event.target as HTMLInputElement
    const files = target.files

    if (files) {
        handleFiles(Array.from(files))
        // 清空input值，允许重复选择同一文件
        target.value = ''
    }
}

// 处理拖拽
const handleDragOver = (event: DragEvent) => {
    event.preventDefault()
    if (!props.disabled) {
        isDragOver.value = true
    }
}

const handleDragLeave = (event: DragEvent) => {
    event.preventDefault()
    isDragOver.value = false
}

const handleDrop = (event: DragEvent) => {
    event.preventDefault()
    isDragOver.value = false

    if (props.disabled) return

    const files = event.dataTransfer?.files

    if (files) {
        handleFiles(Array.from(files))
    }
}

// 处理文件
const handleFiles = (files: File[]) => {
    files.forEach((file, index) => {
        // 检查文件类型
        const isValidType = props.allowedTypes.some(type => {
            if (type.endsWith('/*')) {
                return file.type.startsWith(type.slice(0, -1))
            }
            return file.type === type
        })

        if (!isValidType) {
            ElMessage.error(`不支持的文件类型: ${file.type}`)
            return
        }

        // 检查文件大小
        if (file.size > props.maxSize) {
            ElMessage.error(`文件大小超过限制: ${formatFileSize(props.maxSize)}`)
            return
        }

        // 创建上传文件对象
        const uploadFile: UploadFile = {
            uid: generateUid(),
            name: file.name,
            size: file.size,
            type: file.type,
            status: 'pending',
            raw: file
        }

        // 添加到文件列表
        if (props.multiple) {
            fileList.value.push(uploadFile)
        } else {
            fileList.value = [uploadFile]
        }

        // 自动上传
        uploadFileFunction(uploadFile)
    })
}

// 上传文件
const uploadFileFunction = async (file: UploadFile) => {
    if (!file.raw) {
        return
    }

    file.status = 'uploading'
    file.percentage = 0

    try {
        let response: any

        if (props.customUpload) {
            // 使用自定义上传函数
            const url = await props.customUpload(file.raw)
            response = { url }
        } else {
            // 使用API上传
            response = await uploadFileApi(file.raw, (percentage) => {
                file.percentage = percentage
                // 强制触发响应式更新 - 使用更强制的方法
                const index = fileList.value.findIndex(f => f.uid === file.uid)
                if (index !== -1) {
                    fileList.value[index] = { ...file }
                }
                fileList.value = [...fileList.value]
            })
        }
        console.log(response);

        // 逐个更新属性以确保响应式更新
        file.status = 'success'
        // 从响应对象中提取URL
        file.url = typeof response === 'string' ? response : response.data || response.url
        file.percentage = 100
        file.response = response

        // 调试信息
        console.log('上传成功，文件状态:', file.status)
        console.log('文件URL:', file.url)
        console.log('响应数据:', response)

        // 强制触发响应式更新 - 使用更强制的方法
        const index = fileList.value.findIndex(f => f.uid === file.uid)
        if (index !== -1) {
            fileList.value[index] = { ...file }
        }
        fileList.value = [...fileList.value]

        // 确保DOM更新
        await nextTick()

        emit('success', file, response)
        ElMessage.success('文件上传成功')

    } catch (error) {
        file.status = 'error'
        file.percentage = 0

        // 调试信息
        console.log('上传失败，文件状态:', file.status)
        console.log('错误信息:', error)

        // 强制触发响应式更新 - 使用更强制的方法
        const index = fileList.value.findIndex(f => f.uid === file.uid)
        if (index !== -1) {
            fileList.value[index] = { ...file }
        }
        fileList.value = [...fileList.value]

        // 确保DOM更新
        await nextTick()

        emit('error', file, error)
        ElMessage.error('文件上传失败')
    }
}

// 移除文件
const removeFile = async (index: number) => {
    const file = fileList.value[index]

    if (file.status === 'uploading') {
        await ElMessageBox.confirm('文件正在上传中，确定要取消吗？', '提示', {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
        })
    }

    fileList.value.splice(index, 1)
    emit('remove', file, index)
}

// 预览文件
const handlePreviewFile = (file: UploadFile) => {
    if (!file.url) return

    previewFileData.value = file
    previewUrl.value = file.url
    previewVisible.value = true
}

// 关闭预览
const closePreview = () => {
    previewVisible.value = false
    previewFileData.value = null
    previewUrl.value = ''
}

// 下载文件
const downloadFile = () => {
    if (!previewFileData.value?.url) return

    const link = document.createElement('a')
    link.href = previewFileData.value.url
    link.download = previewFileData.value.name
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
}

// 暴露方法
defineExpose({
    uploadFile: uploadFileFunction,
    removeFile,
    clearFiles: () => {
        fileList.value = []
    }
})
</script>

<style scoped>
.file-upload-container {
    width: 100%;
}

.inline-preview {
    position: relative;
    width: 100%;
    height: 100%;
    min-height: 100px;
    /* height: 200px; */
    border: 1px dashed #dcdfe6;
    border-radius: 8px;
    overflow: hidden;
    background: #fafafa;
    display: flex;
    align-items: center;
    justify-content: center;
}

.inline-preview img {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.inline-preview .inline-remove {
    position: absolute;
    right: 8px;
    top: 8px;
}

.upload-area {
    border: 2px dashed #d9d9d9;
    border-radius: 8px;
    background: #fafafa;
    text-align: center;
    cursor: pointer;
    transition: all 0.3s ease;
    position: relative;
    overflow: hidden;
}

.upload-area:hover {
    border-color: #667eea;
    background: #f0f2ff;
}

.upload-area--dragover {
    border-color: #667eea;
    background: #e6f7ff;
    transform: scale(1.02);
}

.upload-area--disabled {
    cursor: not-allowed;
    opacity: 0.6;
}

.upload-area--disabled:hover {
    border-color: #d9d9d9;
    background: #fafafa;
    transform: none;
}

.upload-content {
    padding: 40px 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
}

.upload-content--plain {
    padding: 24px 12px;
}

.upload-icon {
    font-size: 48px;
    color: #999;
    margin-bottom: 8px;
}

.upload-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.upload-title {
    font-size: 16px;
    font-weight: 500;
    color: #333;
    margin: 0;
}

.upload-desc {
    font-size: 14px;
    color: #666;
    margin: 0;
}

.upload-size {
    font-size: 12px;
    color: #999;
    margin: 0;
}

/* 文件列表 */
.file-list {
    margin-top: 6px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    max-height: 100px;
    overflow-y: auto;
}

.file-item {
    display: flex;
    align-items: center;
    padding: 3px 5px;
    margin-bottom: 1px;
    background: #fff;
    border: 1px solid #e8e8e8;
    border-radius: 3px;
    transition: all 0.3s ease;
    font-size: 11px;
    min-height: 24px;
}

.file-item:hover {
    border-color: #667eea;
    box-shadow: 0 2px 8px rgba(102, 126, 234, 0.1);
}

.file-item--uploading {
    border-color: #667eea;
    background: #f0f2ff;
}

.file-icon {
    width: 24px;
    height: 24px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #f5f5f5;
    border-radius: 6px;
    margin-right: 6px;
    color: #666;
    font-size: 12px;
}

.file-info {
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding-right: 4px;
}

.file-name {
    font-size: 11px;
    width: 80px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex-shrink: 0;
    position: relative;
    cursor: help;
    font-weight: 500;
    color: #333;
}

.file-name:hover {
    background: rgba(0, 0, 0, 0.8);
    color: white;
    padding: 2px 4px;
    border-radius: 3px;
    position: absolute;
    z-index: 1000;
    white-space: nowrap;
    max-width: none;
    overflow: visible;
}

.file-size {
    font-size: 10px;
    color: #999;
    flex-shrink: 0;
    min-width: 35px;
    text-align: right;
}

.file-progress {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-shrink: 0;
    min-width: 60px;
}

.file-progress .el-progress {
    width: 40px;
}

.progress-text {
    font-size: 10px;
    color: #667eea;
    min-width: 20px;
}

.file-status {
    flex-shrink: 0;
    display: flex;
    align-items: center;
}

.file-status .el-icon {
    font-size: 10px;
}

.status-success {
    color: #52c41a;
}

.status-error {
    color: #ff4d4f;
}

.status-uploading {
    color: #667eea;
    animation: spin 1s linear infinite;
}

@keyframes spin {
    from {
        transform: rotate(0deg);
    }

    to {
        transform: rotate(360deg);
    }
}

.file-actions {
    display: flex;
    gap: 1px;
    margin-left: 12px;
}

.file-actions .el-button {
    padding: 1px 3px;
    font-size: 10px;
    min-width: 20px;
    height: 20px;
}

/* 预览对话框 */
.preview-content {
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 300px;
}

.preview-image {
    max-width: 100%;
    max-height: 500px;
    object-fit: contain;
}

.preview-audio {
    width: 100%;
    max-width: 400px;
}

.preview-pdf {
    width: 100%;
    height: 500px;
    border: none;
}

.preview-other {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding: 40px;
}

.preview-icon {
    font-size: 64px;
    color: #999;
}

/* 响应式设计 */
@media (max-width: 768px) {
    .upload-content {
        padding: 30px 16px;
    }

    .upload-icon {
        font-size: 36px;
    }

    .upload-title {
        font-size: 14px;
    }

    .upload-desc,
    .upload-size {
        font-size: 12px;
    }

    .file-item {
        padding: 8px;
    }

    .file-icon {
        width: 32px;
        height: 32px;
        margin-right: 8px;
    }

    .file-name {
        font-size: 13px;
    }

    .file-size {
        font-size: 11px;
    }
}
</style>